import {
  ProColumns,
  ProForm,
  ProFormDependency,
  ProFormDigit,
  ProFormDigitProps,
  ProFormRadio,
  ProFormSegmented,
} from '@ant-design/pro-components';
import {
  ConfigProvider,
  Empty,
  FormInstance,
  FormRule,
  GetProp,
  Space,
  Typography,
} from 'antd';
import { NamePath } from 'antd/es/form/interface';
import { get, merge } from 'lodash-es';
import { AxisBackup } from './AxisBackup';
import { faceEnumsMap, faceInitialValue } from './constants';
import { Axis } from './types';
import { PropsWithChildren, ReactNode, useMemo, useRef } from 'react';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import { isLockAxis } from './utils/axis';
import {
  LimitValue,
  getDepthLimitValue,
  getEndPointInitialValue,
  getRadiusLimitValue,
  getStartPointInitialValue,
} from './utils/form';
import { Vector3 } from 'three';
import { calc } from 'a-calc';

export function useDepthLimit(plankFaceIdParentName: NamePath) {
  const plankFaceId =
    ProForm.useWatch([...plankFaceIdParentName, 'plankFaceId']) ??
    faceInitialValue;
  const form = ProForm.useFormInstance();
  return useMemo(() => {
    const size = form.getFieldValue(['plank', 'size']);
    const depthLimitValue = getDepthLimitValue({ size, plankFaceId });
    return depthLimitValue;
  }, [plankFaceId]);
}

function FormStyleContext({ children }: PropsWithChildren) {
  return (
    <ConfigProvider
      theme={{
        components: {
          Form: {
            itemMarginBottom: 0,
          },
        },
      }}
    >
      {children}
    </ConfigProvider>
  );
}

export function FormPoint({ name: baseName }: { name: NamePath }) {
  function addBaseName(name: NamePath) {
    return [...baseName, name];
  }
  const parentName = baseName.slice(0, -1);
  const isStart = [baseName.at(-1)].flat().at(-1) === 'start';
  const plankFaceId =
    ProForm.useWatch([...parentName, 'plankFaceId']) ?? faceInitialValue;
  const depth = ProForm.useWatch([...parentName, 'depth']) ?? 0;
  const form = ProForm.useFormInstance();
  const { lockAxis, pointInitialValue } = useMemo(() => {
    const size = form.getFieldValue(['plank', 'size']);
    const lockAxis = Object.fromEntries(
      [Axis.x, Axis.y, Axis.z].map((axis) => [
        axis,
        isLockAxis({ axis, plankFaceId }),
      ]),
    );
    let pointInitialValue = isGroove(parentName.at(-1))
      ? getEndPointInitialValue({ size, plankFaceId, depth })
      : getStartPointInitialValue({ size, plankFaceId });
    const pointValue = form.getFieldValue(baseName);
    form.setFieldValue(baseName, merge({}, pointValue, pointInitialValue));
    return { lockAxis, pointInitialValue };
  }, [plankFaceId, depth]);
  const [xInputProps, yInputProps, zInputProps] = [Axis.x, Axis.y, Axis.z].map(
    (axis) => useMathExprInputProps({ name: addBaseName(axis), form }),
  );

  return (
    <FormStyleContext>
      <Space>
        <ProFormDigit
          disabled={lockAxis.x}
          initialValue={pointInitialValue.x}
          rules={[requiredRule]}
          name={addBaseName('x')}
          fieldProps={{
            prefix: <AxisBackup axis={Axis.x} />,
            ...xInputProps,
          }}
        />
        <ProFormDigit
          disabled={lockAxis.y}
          initialValue={pointInitialValue.y}
          rules={[requiredRule]}
          name={addBaseName('y')}
          fieldProps={{
            prefix: <AxisBackup axis={Axis.y} />,
            ...yInputProps,
          }}
        />
        <ProFormDigit
          disabled={lockAxis.z}
          initialValue={pointInitialValue.z}
          rules={[requiredRule]}
          name={addBaseName('z')}
          fieldProps={{
            prefix: <AxisBackup axis={Axis.z} />,
            ...zInputProps,
          }}
        />
      </Space>
    </FormStyleContext>
  );
}
function isGroove(grooveType: string) {
  return grooveType.toLowerCase().includes('groove');
}
function useMathExprInputProps({
  name,
  form,
}: {
  name: NamePath;
  form: FormInstance;
}): GetProp<ProFormDigitProps, 'fieldProps'> {
  const inputElementRef = useRef<HTMLInputElement>(null);
  function onParse() {
    const mathExpr = inputElementRef.current!.value;
    if (!mathExpr) {
      return;
    }
    const value = simpleMathExprParser(mathExpr);
    form.setFieldValue(name, value);
  }
  return {
    ref: inputElementRef,
    onBlur: () => {
      onParse();
    },
    onPressEnter: () => {
      onParse();
    },
  };
}
function simpleMathExprParser(mathExpr?: string) {
  if (!mathExpr) return NaN;
  return Number(calc(mathExpr));
}

function getLastKeywordofName(name: NamePath, keyword: string) {
  return [name.at(-1)].flat().at(-1) === keyword;
}
export function FormSize({ name, desc }: { name: NamePath; desc: string }) {
  const isDepth = getLastKeywordofName(name, 'depth');
  const parentName = name.slice(0, -1);
  const limitValue = useDepthLimit(parentName);
  return (
    <FormStyleContext>
      <ProFormDigit
        {...(isDepth && limitValue)}
        rules={[requiredRule, gtZeroRule]}
        name={name}
        fieldProps={{
          prefix: <span className="text-tertiary/30">{desc}</span>,
        }}
      />
    </FormStyleContext>
  );
}
export const requiredRule: FormRule = {
  required: true,
  message: '必填',
};
export const gtZeroRule: FormRule = {
  validator: (rule, value, callback) => {
    if (value <= 0) {
      callback('录入值需大于0');
    }
    callback();
  },
};
export const createDepthRule: (
  ...params: Parameters<typeof getDepthLimitValue>
) => FormRule = (...params) => {
  const depthLimitValue = getDepthLimitValue(...params);
  return { type: 'number', max: depthLimitValue?.max as number };
};
type GetConfig<T> = T extends (_: any, config: infer C) => any ? C : never;
type Config = GetConfig<ProColumns['formItemProps']>;
export const createRadiusRule: (config: Config) => FormRule = (config) => {
  const { width, start, end } = config.entity;
  const height = new Vector3(start.x, start.y, start.z).distanceTo(
    new Vector3(end.x, end.y, end.z),
  );
  const limitValue = getRadiusLimitValue({ rect: { width, height } });
  return { type: 'number', ...limitValue };
};
export function FormFace({ label, name }: { label: string; name: NamePath }) {
  return (
    <ProFormRadio.Group
      label={label}
      initialValue={faceInitialValue}
      name={name}
      valueEnum={faceEnumsMap}
      fieldProps={{
        optionType: 'button',
        className: 'flex *:w-full *:text-center',
      }}
    />
  );
}
const useCountStyle = createStyles(({ token }) => ({
  color: { color: token.colorPrimary },
  size: { fontSize: token.sizeSM },
}));
function Count({
  value,
  renderItem,
}: {
  value: number;
  renderItem?: (value: number) => ReactNode;
}) {
  const { styles, cx } = useCountStyle();

  return (
    <span className={cx(styles.color, styles.size)}>
      {renderItem ? renderItem(value) : value}
    </span>
  );
}

export function FormCount({ name }: { name: NamePath }) {
  const { styles: countStyles } = useCountStyle();
  return (
    <ProFormDependency name={[name]}>
      {(values) => {
        const items: any[] = get(values, name, []);
        return (
          <Typography.Text type="secondary" className={countStyles.size}>
            共 {<Count value={items.length} />} 项
          </Typography.Text>
        );
      }}
    </ProFormDependency>
  );
}
const formTitleStyle = createStyles(({ token }) => ({
  margin: { marginBottom: token.marginXXS },
}));
export function FormTitle({
  title,
  extra,
  className,
}: {
  title: string;
  extra?: ReactNode;
  className?: string;
}) {
  const { styles } = formTitleStyle();

  return (
    <ConfigProvider theme={{ token: { fontSize: 14 } }}>
      <Space
        className={classNames(styles.margin, className, 'flex justify-between')}
      >
        <Typography.Text strong>{title}</Typography.Text>
        {extra}
      </Space>
    </ConfigProvider>
  );
}
export function FormEmpty({
  name,
  label,
  emptyRender,
  noEmptyRender,
}: {
  name: NamePath;
  label?: string;
  emptyRender?: (node: ReactNode) => React.ReactNode;
  noEmptyRender?: () => React.ReactNode;
}) {
  return (
    <ProFormDependency name={[name]}>
      {(values) => {
        const items: any[] = get(values, name, []);
        const isEmpty = items.length === 0;
        return isEmpty
          ? emptyRender?.(
              <Empty description={`暂无${label ?? ''}数据，请添加`} />,
            )
          : noEmptyRender?.();
      }}
    </ProFormDependency>
  );
}
